#include "BinObjReader.h"
#include "IniFile.h"
#include "MeshList.h"
#include "TriangleList.h"
#include "Material.h"
#include "MaterialReader.h"
#include "Skeleton.h"
#include "BoneReader.h"
#include "ObjMesh.h"

BinObjReader::BinObjReader(IniFile* gameConfig, tstring filename):m_pGameConfig(gameConfig),m_pCurrentMesh(NULL),m_pMeshList(NULL),m_NrOfBytesRead(0),m_pMaterialReader(0),m_pCurrentSkeleton(0)
{
	tstring basedir = gameConfig->GetValue(_T("Export"),_T("baseDir"));
	
	tstring binMeshDir = gameConfig->GetValue(_T("Export"),_T("meshBinDir"));
	this->SetFileName(basedir + _T("/") + binMeshDir+_T("/")+filename);

	m_TextureDir = basedir + _T("/") + gameConfig->GetValue(_T("Export"),_T("textureDir"));
	m_RigDir = basedir + _T("/")+gameConfig->GetValue(_T("Export"),_T("skeletonDir"));

}

BinObjReader::~BinObjReader(void)
{
	delete m_pMaterialReader;
}


bool BinObjReader::Read(){
	if ( this->Open() ){
		m_pMeshList = new MeshList();
		while( ! m_File.eof()){
			OBJECTTYPE header;
			m_File.read((char*)&header,sizeof(OBJECTTYPE));
			if ( m_File.eof() )
				break;
			m_NrOfBytesRead+=sizeof(OBJECTTYPE);
			if (header == VERTEXLIST ){
				m_pCurrentMesh = new ObjMesh();
				m_pMeshList->AddMesh(m_pCurrentMesh);
				ReadVertexList();
			}else if (header == TRIANGLELIST ){
				ReadTriangleList();
			}else if (header == MATERIALFILE ){
				ReadMaterialFile();
			}
		}
		this->Close();
		return true;
	}else
		return false;
}

void BinObjReader::ReadMaterialFile()
{
	tstring materialFile = ReadString();
	m_pMaterialReader = new MaterialReader(m_pGameConfig,m_TextureDir+materialFile);
	m_pMaterialReader->Read();
}

void BinObjReader::ReadVertexList()
{
	OBJECTTYPE type;
	tstring skeletonFile;

	m_File.read((char*)&type,sizeof(OBJECTTYPE));
	if (type == SKELETONFILE ){
		skeletonFile = ReadString();
		BoneReader br(m_RigDir+_T("/")+skeletonFile);
		br.Read();
		m_pCurrentSkeleton = br.GetSkeleton();
		m_pCurrentSkeleton->SetFileLocation(skeletonFile);
		m_File.read((char*)&type,sizeof(OBJECTTYPE));
		m_pCurrentMesh->SetSkeleton(m_pCurrentSkeleton);
	}

	if ( type == VERTEXDATA)
	{
		int sizeInBytes,nrOfVertices,numSemantics;
		vector<SEMANTIC> semantics;
		vector<int> sizes;

		m_File.read((char*)&sizeInBytes,sizeof(int));
		m_File.read((char*)&nrOfVertices,sizeof(int));
		m_File.read((char*)&numSemantics,sizeof(int));

		m_NrOfBytesRead+= 3*sizeof(int);

		for (int i = 0 ; i< numSemantics; ++i)
		{
			SEMANTIC s;
			m_File.read((char*)&s, sizeof(SEMANTIC));
			int size;
			m_File.read((char*)&size,sizeof(int));
			semantics.push_back(s);
			sizes.push_back(size);

			m_NrOfBytesRead+= sizeof(SEMANTIC);
			m_NrOfBytesRead+= sizeof(int);
		}
		VertexList* vertexList = new VertexList(nrOfVertices,semantics,sizes,true);
		m_File.read((char*)vertexList->GetData(),sizeInBytes);
		m_NrOfBytesRead+= sizeInBytes;
		m_pCurrentMesh->SetVertexList(vertexList);
	}
}

void BinObjReader::ReadTriangleList()
{
	int sizeInBytes, nrOfTriangles;

	// read material
	OBJECTTYPE type;
	m_File.read((char*)&type,sizeof(OBJECTTYPE));
	tstring matName;
	if ( type == MATERIALNAME ){
		matName = ReadString();
		m_File.read((char*)&type, sizeof(OBJECTTYPE));
	}
	if (type == TRIANGLEDATA )
	{

		m_File.read((char*)&sizeInBytes,sizeof(int));
		m_File.read((char*)&nrOfTriangles,sizeof(int));

		m_NrOfBytesRead+= 2*sizeof(int);
		TriangleList* tl = new TriangleList(nrOfTriangles);

		m_File.read( (char*)tl->GetData(),sizeInBytes);
		tl->UpdateIndices();

		m_NrOfBytesRead+= sizeInBytes;

		

		Material* material = NULL;
		bool hasSkeleton = m_pCurrentMesh->GetSkeleton() != NULL ;
		if ( m_pMaterialReader->HasMaterial(matName))
		{
			material = m_pMaterialReader->GetMaterial(matName);
			material->SetUseVertexSkinning(hasSkeleton);
		}else{
			material = new Material(_T("test"));
			material->SetDiffuseTexture(m_TextureDir+_T("/models/dry_dirt.dds"));
			material->SetUseVertexSkinning(hasSkeleton);
		}
		IndexBufferMaterial* ibm= new IndexBufferMaterial(tl,material);
		m_pCurrentMesh->AddIndexBuffer(ibm);
	}
}